package com.luoyuanxiangvip.common.aspect;

import com.luoyuanxiangvip.common.exception.CheckedException;
import com.luoyuanxiangvip.common.exception.ExpiredException;
import com.luoyuanxiangvip.common.exception.InvalidException;
import com.luoyuanxiangvip.common.exception.PasswordException;
import com.luoyuanxiangvip.common.utils.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 处理和包装异常
 * </p>
 *
 * @author luoyuanxiang <p>luoyuanxiangvip.com</p>
 * @since 2019/7/6
 */
@Slf4j
@Aspect
@Component
public class ControllerAspect {

    @Pointcut("execution(public com.luoyuanxiangvip.common.utils.AjaxResult *(..))")
    public void controllerAspect() {}

    /**
     * 在切点之前织入
     * @param joinPoint joinPoint
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint){
        // 开始打印请求日志
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 打印请求相关参数
        log.info("========================================== Start ==========================================");
        // 打印请求 url
        log.info("URL            : {}", request.getRequestURL().toString());
        // 打印 Http method
        log.info("HTTP Method    : {}", request.getMethod());
        // 打印调用 controller 的全路径以及执行方法
        log.info("Class Method   : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        // 打印请求的 IP
        log.info("IP             : {}", request.getRemoteAddr());
    }

    @Around("controllerAspect()")
    public Object handlerControllerMethod(ProceedingJoinPoint pjp) {
        long startTime = System.currentTimeMillis();
        AjaxResult result;
        try {
            result = (AjaxResult) pjp.proceed();
            log.info(pjp.getSignature() + " use time:{} ms", (System.currentTimeMillis() - startTime));
        } catch (Throwable e) {
            result = handlerException(pjp, e);
        }
        return result;
    }

    /**
     * 在切点之后织入
     */
    @After("controllerAspect()")
    public void doAfter(){
        log.info("=========================================== End ===========================================");
        // 每个请求之间空一行
        log.info("");
    }

    /**
     * 封装异常信息
     * @param e 异常
     * @return AjaxResult
     */
    private AjaxResult handlerException(ProceedingJoinPoint point, Throwable e) {
        log.error("异常信息", e);
        // 开始打印请求日志
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 打印请求 url
        log.error("请求错误URL            : {}", request.getRequestURL().toString());
        // 打印 Http method
        log.error("请求错误HTTP Method    : {}", request.getMethod());
        // 打印调用 controller 的全路径以及执行方法
        log.error("请求错误Class Method   : {}.{}", point.getSignature().getDeclaringTypeName(), point.getSignature().getName());
        // 已知异常
        if(e instanceof AuthenticationException) {
            Throwable cause = e.getCause();
            // 密码错误异常
            if(cause instanceof PasswordException) {
                return AjaxResult.error(e.getCause().getMessage());
            }
            // token 过期异常
            if(cause instanceof ExpiredException) {
                return AjaxResult.error(50014,e.getCause().getMessage());
            }
            // token 无效异常
            if(cause instanceof InvalidException) {
                return AjaxResult.error(50008,e.getCause().getMessage());
            }
            // 参数效验
        } else if(e instanceof CheckedException) {
            return AjaxResult.error(e.getMessage());
        } else if(e instanceof BindException) {
            return AjaxResult.error(message(((BindException)e).getBindingResult()));
        } else if(e instanceof MethodArgumentNotValidException) {
            return AjaxResult.error(message(((MethodArgumentNotValidException)e).getBindingResult()));
        } else if(e instanceof ConstraintViolationException) {
            List<String> defaultMsg = ((ConstraintViolationException) e).getConstraintViolations()
                    .stream()
                    .map(ConstraintViolation::getMessage)
                    .collect(Collectors.toList());
            return AjaxResult.error(defaultMsg.get(0));
        }
        return AjaxResult.error();
    }

    private String message(BindingResult e){
        List<String> defaultMsg = e.getAllErrors()
                .stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.toList());
        return defaultMsg.get(0);
    }
}
